module WsClient
	
	# Methods defined in a subclass:
	# - create_frame(message, options = {})
	# - read_frame
	# - close(server_initiated)
	# - create_handshake(options)
	class ProtocolBase
		include Timeout
	
		attr_reader :socket, :url
		
		def initialize(url)
			@url = WsClient::Url.new(url)
			@socket = SocketWrapper.new { TCPSocket.open(@url.host, @url.port) }
		end
		
		def send_message(message, options = {})
			frame = create_frame(message, options)
			send_frame(frame)
			frame
		end
		
		def send_frame(frame)
			@socket.send(frame.to_s, 0)
		end
		
		def handshake(options)
			hs = create_handshake(options)
			@socket.send(hs.to_s, 0)
			resp = WsClient::HandshakeResponse.read(@socket)
			
			unless hs.valid_response?(resp)
				raise WsClient::HandshakeError.new("Invalid handshake response received: #{hs.errors.join(', ')}")
			end
			
			return hs, resp
		end
		
		def close_socket
			begin
				@socket.close if @socket
			rescue Exception
			end
		end
	end
	
	private
	
	class SocketWrapper < BasicObject
		attr_reader :socket
	
		def initialize(&block)
			@socket = _error(&block)
		end
		
		def respond_to?(name)
			@socket.respond_to?(name)
		end
		
		def method_missing(name, *args)
			_error { @socket.__send__(name, *args) }
		end
		
		private
		
		def _error
			begin
				yield
			rescue ::SystemCallError => err
				error = ::WsClient::WebSocketError.new(err.message)
				error.set_backtrace(err.backtrace)
				raise error
			end
		end
	end
end
